import random
from typing import Union

import fake_useragent
import requests
import urllib3
import config
from bs4 import BeautifulSoup
from lxml import etree

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class WebScraper:
	ACCEPTED_METHODS = {"GET", "POST"}  # 接受的请求方法集合
	ACCEPTED_PARSE_METHODS = {"html", "BeautifulSoup", "lxml", "bin"}  # 接受的解析方法集合

	def __init__(self):
		self.ua = fake_useragent.UserAgent()
		self.proxies = {"http": config.http_proxy, "https": config.https_proxy}

	def generate_user_agent(self, browsers: list = None):
		if browsers is None:
			return self.ua.random
		if not isinstance(browsers, list):
			raise ValueError("browsers must be a list")
		accepted_browsers = {"chrome", "edge", "ie", "firefox", "safari", "opera"}
		if not set(browsers).issubset(accepted_browsers):
			raise ValueError("Invalid browser name. Accepted values: chrome, edge, ie, firefox, safari, opera")
		return self.ua.__getattr__(random.choice(browsers))

	def send_request(self, url: str, method: str = "get", headers: dict = None, encoding: str = "utf-8",
					 params: dict = None, data: Union[dict, str] = None, cookies: dict = None, verify: bool = False,
					 parse_method: str = "html", **kwargs):
		method = method.upper()
		if method not in self.ACCEPTED_METHODS:
			raise ValueError("Invalid method. Accepted values:{0}".format(', '.join(self.ACCEPTED_METHODS)))

		if parse_method not in self.ACCEPTED_PARSE_METHODS:
			raise ValueError("Invalid parse method. Accepted values:{0}".format(', '.join(self.ACCEPTED_PARSE_METHODS)))

		headers = headers or {}
		headers["User-Agent"] = self.generate_user_agent()

		res = requests.request(method, url, verify = verify, headers = headers, params = params, data = data,
							   cookies = cookies, proxies = self.proxies, **kwargs)
		return self.parse_response(res, encoding, parse_method)

	def parse_response(self, response, encoding = "utf-8", parse_method = "html"):
		parse_methods = {
			"html": lambda: response.text,
			"BeautifulSoup": lambda: BeautifulSoup(response.text, "html.parser"),
			"lxml": lambda: etree.HTML(response.text),
			"bin": lambda: response.content
		}
		if parse_method not in parse_methods:
			raise ValueError("Invalid parse method. Accepted values: {}".format(', '.join(self.ACCEPTED_PARSE_METHODS)))
		if parse_method != "bin":
			response.encoding = encoding
		return parse_methods[parse_method]()
